module ysyx_210443(
    input         clock,
    input         reset,
    input         io_interrupt,
    input         io_master_awready,
    output        io_master_awvalid,
    output [31:0] io_master_awaddr,
    output [3:0]  io_master_awid,
    output [7:0]  io_master_awlen,
    output [2:0]  io_master_awsize,
    output [1:0]  io_master_awburst,
    input         io_master_wready,
    output        io_master_wvalid,
    output [63:0] io_master_wdata,
    output [7:0]  io_master_wstrb,
    output        io_master_wlast,
    output        io_master_bready,
    input         io_master_bvalid,
    input  [1:0]  io_master_bresp,
    input  [3:0]  io_master_bid,
    input         io_master_arready,
    output        io_master_arvalid,
    output [31:0] io_master_araddr,
    output [3:0]  io_master_arid,
    output [7:0]  io_master_arlen,
    output [2:0]  io_master_arsize,
    output [1:0]  io_master_arburst,
    output        io_master_rready,
    input         io_master_rvalid,
    input  [1:0]  io_master_rresp,
    input  [63:0] io_master_rdata,
    input         io_master_rlast,
    input  [3:0]  io_master_rid,
    output        io_slave_awready,
    input         io_slave_awvalid,
    input  [31:0] io_slave_awaddr,
    input  [3:0]  io_slave_awid,
    input  [7:0]  io_slave_awlen,
    input  [2:0]  io_slave_awsize,
    input  [1:0]  io_slave_awburst,
    output        io_slave_wready,
    input         io_slave_wvalid,
    input  [63:0] io_slave_wdata,
    input  [7:0]  io_slave_wstrb,
    input         io_slave_wlast,
    input         io_slave_bready,
    output        io_slave_bvalid,
    output [1:0]  io_slave_bresp,
    output [3:0]  io_slave_bid,
    output        io_slave_arready,
    input         io_slave_arvalid,
    input  [31:0] io_slave_araddr,
    input  [3:0]  io_slave_arid,
    input  [7:0]  io_slave_arlen,
    input  [2:0]  io_slave_arsize,
    input  [1:0]  io_slave_arburst,
    input         io_slave_rready,
    output        io_slave_rvalid,
    output [1:0]  io_slave_rresp,
    output [63:0] io_slave_rdata,
    output        io_slave_rlast,
    output [3:0]  io_slave_rid
);


    // Pc
    // Pc -> If, Mux_alu_ia, Mux_offset
    wire [`BUS_WIDTH] inst_addr;

    // Inst_rom
    // Inst_rom -> Id, Ie
    wire [31 :     0] inst;
    
    // Ctr_cpu
    // Ctr_cpu -> Ie
    wire [2 :      0] ext_op;
    // Ctr_cpu -> Reg
    wire              reg_wen;
    // Ctr_cpu -> Alu
    wire [3 :      0] alu_ctr;
    // Ctr_cpu -> Ctr_mem, Data_mem
    wire              mem_ren;
    wire              mem_wen;
    // Ctr_cpu -> Mux_alu_ia
    wire              alu_asrc;
    // Ctr_cpu -> Mux_alu_ib
    wire [1 :      0] alu_bsrc;
    // Ctr_cpu -> Mux_w_data
    wire              mem2reg;
    // Ctr_cpu -> Mux_offset
    wire [1 :      0] pc_sel;
    // Ctr_cpu -> Ctr_mem, Mux_width
    wire [2 :      0] width_sel;

    // Ctr_mem
    // Ctr_mem -> Data_mem
    wire [`BUS_WIDTH] mem_wdata;
    wire [`BUS_WIDTH] mem_wmask;
    // Ctr_mem -> Mux_w_data
    wire [`BUS_WIDTH] data_out;
    
    // Id
    // Id -> Reg_file
    wire [4 :      0] rs1;
    wire [4 :      0] rs2;
    wire [4 :      0] rd;
    // Id -> Ctr_cpu
    wire [4 :      0] op;
    wire [2 :      0] f3;
    wire              f7;

    // Reg_file
    // Reg_file -> Mux_alu_ia, Mux_offset
    wire [`BUS_WIDTH] r_data1;
    // Reg_file -> Ctr_mem, Mux_alu_ib
    wire [`BUS_WIDTH] r_data2;
    // Reg_file -> Difftest
    wire [`BUS_WIDTH] regs_o[31 : 0];
    
    // Ie
    // Ie -> Mux_alu_ib, Mux_offset
    wire [`BUS_WIDTH] imm;

    // Mux_alu_ia
    // Mux_alu_ia -> Alu
    wire [`BUS_WIDTH] alu_ia;

    // Mux_alu_ib
    // Mux_alu_ib -> Alu
    wire [`BUS_WIDTH] alu_ib;

    // Alu
    // Alu -> Ctr_cpu, Ctr_mem, Mux_w_data, Data_mem
    wire [`BUS_WIDTH] result;
    // Alu -> Ctr_cpu
    wire              zero;

    // Data_mem
    // Data_mem -> Mux_w_data
    wire [`BUS_WIDTH] mem_rdata;

    // Mux_w_data
    // Mux_w_data -> Ctr_mem
    wire [`BUS_WIDTH] mux_width_i;

    // Mux_width
    // Mux_width -> Reg_file
    wire [`BUS_WIDTH] w_data;

    // Mux_offset
    // Mux_offset -> Pc
    wire [`BUS_WIDTH] offset;


    ysyx_210443_pc Pc (
        .clk            (clock),
        .rst            (reset),

        .offset         (offset),
        .inst_addr      (inst_addr)
    );

    ysyx_210443_inst_rom Inst_rom (
        .clk            (clock),
        .rst            (reset),
        
        .inst_addr      (inst_addr),
        .inst           (inst)
    );
    
    ysyx_210443_ctr_cpu Ctr_cpu (
        .rst            (reset),
        
        .op             (op),
        .f3             (f3),
        .f7             (f7),

        .zero           (zero),
        .result         (result),

        .ext_op         (ext_op),
        .reg_wen        (reg_wen),
        .alu_ctr        (alu_ctr),
        .mem_ren        (mem_ren),
        .mem_wen        (mem_wen),
        .alu_asrc       (alu_asrc),
        .alu_bsrc       (alu_bsrc),
        .mem2reg        (mem2reg),
        .pc_sel         (pc_sel),
        .width_sel      (width_sel)
    );

    ysyx_210443_ctr_mem Ctr_mem (
        .rst            (reset),

        .mem_ren        (mem_ren),
        .mem_wen        (mem_wen),
        .addr           (result),
        .data_in        (r_data2),
        .width_sel      (width_sel),
        .data_out       (data_out),

        .mem_rdata      (mem_rdata),
        .mem_wdata      (mem_wdata),
        .mem_wmask      (mem_wmask)
    );
    
    ysyx_210443_id Id (
        .rst            (reset),
        
        .inst           (inst),

        .op             (op),
        .f3             (f3),
        .f7             (f7),
        .rs1            (rs1),
        .rs2            (rs2),
        .rd             (rd)
    );

    ysyx_210443_reg_file Reg_file (
        .clk            (clock),
        .rst            (reset), 

        .w_addr         (rd),
        .w_data         (w_data),
        .reg_wen        (reg_wen),

        .r_addr1        (rs1),
        .r_data1        (r_data1),

        .r_addr2        (rs2),
        .r_data2        (r_data2),

        .regs_o         (regs_o)
    );

    ysyx_210443_ie Ie (
        .rst            (reset),
        
        .inst           (inst),
        .ext_op         (ext_op),

        .imm            (imm)
    );

    ysyx_210443_mux_alu_ia Mux_alu_ia (
        .rst            (reset),
        
        .inst_addr      (inst_addr),
        .r_data1        (r_data1),
        .alu_asrc       (alu_asrc),

        .alu_ia         (alu_ia)
    );

    ysyx_210443_mux_alu_ib Mux_alu_ib (
        .rst            (reset),
        
        .r_data2        (r_data2),
        .imm            (imm),
        .alu_bsrc       (alu_bsrc),

        .alu_ib         (alu_ib)
    );

    ysyx_210443_alu Alu (
        .rst            (reset),
        
        .alu_ia         (alu_ia),
        .alu_ib         (alu_ib),
        .alu_ctr        (alu_ctr),

        .result         (result),
        .zero           (zero)
    );

    ysyx_210443_data_mem Data_mem (
        .clk            (clock),
        .rst            (reset), 
        
        .addr           (result),
        .mem_wdata      (mem_wdata),
        .mem_ren        (mem_ren),
        .mem_wmask      (mem_wmask),
        .mem_wen        (mem_wen),

        .mem_rdata      (mem_rdata)
    );

    ysyx_210443_mux_w_data Mux_w_data (
        .rst            (reset),
        
        .result         (result),
        .data_out       (data_out),
        .mem2reg        (mem2reg),

        .w_data         (mux_width_i)
    );

    ysyx_210443_mux_width Mux_width (
        .rst            (reset),
        
        .mux_width_i    (mux_width_i),
        .width_sel      (width_sel),

        .mux_width_o    (w_data)
    );

    ysyx_210443_mux_offset Mux_offset (
        .rst            (reset),
        
        .imm            (imm),
        .r_data1        (r_data1),
        .inst_addr      (inst_addr),
        .pc_sel         (pc_sel),

        .offset         (offset)
    );


    always @(negedge clock) begin
        if (reset) begin
            io_master_awvalid <= 0;
            io_master_awaddr  <= 0;
            io_master_awid    <= 0;   
            io_master_awlen   <= 0;
            io_master_awsize  <= 0;
            io_master_awburst <= 0;
            io_master_wvalid  <= 0;
            io_master_wdata   <= 0;
            io_master_wstrb   <= 0;
            io_master_wlast   <= 0;
            io_master_bready  <= 0;
            io_master_arvalid <= 0;
            io_master_araddr  <= 0;
            io_master_arid    <= 0;
            io_master_arlen   <= 0;
            io_master_arsize  <= 0;
            io_master_arburst <= 0;
            io_master_rready  <= 0;
            io_slave_awready  <= 0;
            io_slave_wready   <= 0;
            io_slave_bvalid   <= 0;
            io_slave_bresp    <= 0;
            io_slave_bid      <= 0;
            io_slave_arready  <= 0;
            io_slave_rvalid   <= 0;
            io_slave_rresp    <= 0;
            io_slave_rdata    <= 0;
            io_slave_rlast    <= 0;
            io_slave_rid      <= 0;
        end
        else begin
            io_master_awvalid <= 1'b1;
            io_master_awaddr  <= 0;
            io_master_awid    <= 0;   
            io_master_awlen   <= 0;
            io_master_awsize  <= 0;
            io_master_awburst <= 0;
            io_master_wvalid  <= 0;
            io_master_wdata   <= 0;
            io_master_wstrb   <= 0;
            io_master_wlast   <= 0;
            io_master_bready  <= 0;
            io_master_arvalid <= 0;
            io_master_araddr  <= 0;
            io_master_arid    <= 0;
            io_master_arlen   <= 0;
            io_master_arsize  <= 0;
            io_master_arburst <= 0;
            io_master_rready  <= 0;
            io_slave_awready  <= 0;
            io_slave_wready   <= 0;
            io_slave_bvalid   <= 0;
            io_slave_bresp    <= 0;
            io_slave_bid      <= 0;
            io_slave_arready  <= 0;
            io_slave_rvalid   <= 0;
            io_slave_rresp    <= 0;
            io_slave_rdata    <= 0;
            io_slave_rlast    <= 0;
            io_slave_rid      <= 0;        
        end
    end


endmodule;


`timescale 1ns/1ps

`define ZERO_WORD 64'b0
`define PC_START  64'h00000000_80000000  
`define BUS_WIDTH 63 : 0


module ysyx_210443_alu (
    input  wire              rst,
    
    input  wire [`BUS_WIDTH] alu_ia,
    input  wire [`BUS_WIDTH] alu_ib,
    input  wire [3 :      0] alu_ctr,

    output reg  [`BUS_WIDTH] result,
    output reg               zero
);


    wire signed [`BUS_WIDTH] alu_ia_signed;
    wire signed [`BUS_WIDTH] alu_ib_signed;

    assign alu_ia_signed = alu_ia;
    assign alu_ib_signed = alu_ib;
    
    always @(*) begin
        if (rst) begin
            result = `ZERO_WORD;
            zero   = 1'b0;
        end
        else begin
            case (alu_ctr)
                4'b0000: begin    // add 加法运算
                    result = alu_ia + alu_ib;
                end
                4'b0001: begin    // sll 逻辑左移
                    result = alu_ia << alu_ib[5 : 0];
                end
                4'b0010: begin    // slt 带符号整数比较，小于置1
                    result = (alu_ia_signed < alu_ib_signed) ? 64'b1 : `ZERO_WORD;
                end
                4'b0011: begin    // sltu 无符号整数比较，小于置1
                    result = (alu_ia < alu_ib) ? 64'b1 : `ZERO_WORD;
                end
                4'b0100: begin    // srl 逻辑右移
                    result = alu_ia >> alu_ib[5 : 0];
                end
                4'b0101: begin    // sra 算术右移
                    result = ({64{alu_ia[63]}} << (6'b11_1111 - alu_ib[5 : 0])) | (alu_ia >> alu_ib[5 : 0]);
                end
                4'b0110: begin    // or 按位或运算
                    result = alu_ia | alu_ib;
                end
                4'b0111: begin    // xor 按位异或运算
                    result = alu_ia ^ alu_ib;
                end
                4'b1000: begin    // sub 减法运算
                    result = (alu_ia - alu_ib);
                end
                4'b1001: begin    // and 按位与运算
                    result = alu_ia & alu_ib;
                end
                4'b1010: begin    // sllw 截取低32位逻辑左移，结果零扩展
                    result = {32'b0, (alu_ia[31 : 0] << alu_ib[4 : 0])};
                end
                4'b1011: begin    // srlw 截取低32位逻辑右移，结果零扩展
                    result = {32'b0, (alu_ia[31 : 0] >> alu_ib[4 : 0])};
                end
                4'b1100: begin    // sraw 截取低32位算术右移，结果零扩展
                    result = {32'b0, ({32{alu_ia[31]}} << (5'b11111 - alu_ib[4 : 0])) | (alu_ia[31 : 0] >> alu_ib[4 : 0])};
                end
                4'b1111: begin    // srcb 选择操作数b直接输出
                    result = alu_ib;
                end
                default: begin
                    result = `ZERO_WORD;
                end
            endcase
            zero = (result == 64'b0) ? 1'b1 : 1'b0;     // 零标志
        end    
    end


endmodule


module ysyx_210443_ctr_cpu (
    input  wire              rst,
    
    input  wire [4 :      0] op,
    input  wire [2 :      0] f3,
    input  wire              f7,
    
    input  wire              zero,
    input  wire [`BUS_WIDTH] result,

    output reg  [2 :      0] ext_op,
    output reg               reg_wen,
    output reg  [3 :      0] alu_ctr,
    output reg               mem_ren,    
    output reg               mem_wen,
    output reg               alu_asrc,
    output reg  [1 :      0] alu_bsrc,
    output reg               mem2reg,
    output reg  [1 :      0] pc_sel,
    output reg  [2 :      0] width_sel
);
    

    always @(*) begin
        if (rst) begin
            ext_op    = 3'b000;
            reg_wen   = 1'b0;
            alu_ctr   = 4'b0000;
            mem_ren   = 1'b0;
            mem_wen   = 1'b0;
            alu_asrc  = 1'b0;
            alu_bsrc  = 2'b00;
            mem2reg   = 1'b0;
            pc_sel    = 2'b00;
            width_sel = 3'b000;
        end
        else begin
            case (op)
                5'b01100: begin
                    ext_op    = 3'b111;    // 可取任意值
                    reg_wen   = 1'b1;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b0;
                    alu_bsrc  = 2'b00;
                    mem2reg   = 1'b0;
                    pc_sel    = 2'b00;
                    width_sel = 3'b011;
                    case (f3)
                        3'b001: begin    // sll
                            alu_ctr = 4'b0001;
                        end
                        3'b101: begin    // sra, srl
                            alu_ctr = (f7) ? 4'b0101 : 4'b0100;
                        end
                        3'b000: begin    // sub, add
                            alu_ctr = (f7) ? 4'b1000 : 4'b0000;
                        end
                        3'b100: begin    // xor
                            alu_ctr = 4'b0111;
                        end
                        3'b110: begin    // or
                            alu_ctr = 4'b0110;
                        end
                        3'b111: begin    // and
                            alu_ctr = 4'b1001;
                        end
                        3'b010: begin    // slt
                            alu_ctr = 4'b0010;
                        end
                        3'b011: begin    // sltu
                            alu_ctr = 4'b0011;
                        end
                    endcase
                end
                5'b00100: begin
                    reg_wen   = 1'b1;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b0;
                    alu_bsrc  = 2'b10;
                    mem2reg   = 1'b0;
                    pc_sel    = 2'b00;
                    width_sel = 3'b011;
                    case (f3)
                        3'b001: begin    // slli
                            ext_op  = 3'b000;
                            alu_ctr = 4'b0001;
                        end
                        3'b101: begin    // srai,srli
                            ext_op  = 3'b000;
                            alu_ctr = (f7) ? 4'b0101 : 4'b0100;
                        end
                        3'b000: begin    // addi
                            ext_op  = 3'b000;
                            alu_ctr = 4'b0000;
                        end
                        3'b100: begin    // xori
                            ext_op  = 3'b000;
                            alu_ctr = 4'b0111;
                        end
                        3'b110: begin    // ori
                            ext_op  = 3'b000;
                            alu_ctr = 4'b0110;
                        end
                        3'b111: begin    // andi
                            ext_op  = 3'b000;
                            alu_ctr = 4'b1001;
                        end
                        3'b010: begin    // slti
                            ext_op  = 3'b000;
                            alu_ctr = 4'b0010;
                        end
                        3'b011: begin    // sltiu
                            ext_op  = 3'b000;
                            alu_ctr = 4'b0011;
                        end
                    endcase
                end
                5'b01110: begin
                    ext_op    = 3'b111;    // 可取任意值
                    reg_wen   = 1'b1;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b0;
                    alu_bsrc  = 2'b00;
                    mem2reg   = 1'b0;
                    pc_sel    = 2'b00;
                    width_sel = 3'b010;
                    case (f3)
                        3'b001: begin    // sllw
                            alu_ctr = 4'b1010;
                        end
                        3'b101: begin    // sraw, srlw
                            alu_ctr = (f7) ? 4'b1100 : 4'b1011;
                        end
                        3'b000: begin    // subw, addw
                            alu_ctr = (f7) ? 4'b1000 : 4'b0000;
                        end
                        default: begin
                            alu_ctr = 4'b0000;
                        end
                    endcase
                end
                5'b00110: begin
                    reg_wen   = 1'b1;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b0;
                    alu_bsrc  = 2'b10;
                    mem2reg   = 1'b0;
                    pc_sel    = 2'b00;
                    width_sel = 3'b010;
                    case (f3)
                        3'b001: begin    // slliw
                            ext_op  = 3'b000;
                            alu_ctr = 4'b1010;
                        end
                        3'b101: begin    // sraiw, srliw
                            ext_op  = 3'b000;
                            alu_ctr = (f7) ? 4'b1100 : 4'b1011;
                        end
                        3'b000: begin    // addiw
                            ext_op  = 3'b000;
                            alu_ctr = 4'b0000;
                        end
                        default: begin
                            ext_op  = 3'b111;
                            alu_ctr = 4'b0000;
                        end
                    endcase
                end
                5'b01101: begin    // lui
                    ext_op    = 3'b001;
                    reg_wen   = 1'b1;
                    alu_ctr   = 4'b1111;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b0;    // 可取任意值
                    alu_bsrc  = 2'b10;
                    mem2reg   = 1'b0;
                    pc_sel    = 2'b00;
                    width_sel = 3'b011;
                end
                5'b00101: begin    // auipc
                    ext_op    = 3'b001;
                    reg_wen   = 1'b1;
                    alu_ctr   = 4'b0000;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b1;
                    alu_bsrc  = 2'b10;
                    mem2reg   = 1'b0;
                    pc_sel    = 2'b00;
                    width_sel = 3'b011;
                end
                5'b11000: begin    // Branches
                    ext_op    = 3'b011;
                    reg_wen   = 1'b0;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b0;
                    alu_bsrc  = 2'b00;
                    mem2reg   = 1'b0;      // 可取任意值
                    width_sel = 3'b011;    // 可取任意值
                    case (f3)
                        3'b000: begin    // beq
                            alu_ctr = 4'b1000;
                            pc_sel  = ( zero     ) ? 2'b01 : 2'b00;
                        end
                        3'b001: begin    // bne
                            alu_ctr = 4'b1000;
                            pc_sel  = (~zero     ) ? 2'b01 : 2'b00;
                        end
                        3'b100: begin    // blt
                            alu_ctr = 4'b0010;
                            pc_sel  = ( result[0]) ? 2'b01 : 2'b00;
                        end
                        3'b101: begin    // bge
                            alu_ctr = 4'b0010;
                            pc_sel  = (~result[0]) ? 2'b01 : 2'b00;
                        end
                        3'b110: begin    // bltu
                            alu_ctr = 4'b0011;
                            pc_sel  = ( result[0]) ? 2'b01 : 2'b00;
                        end
                        3'b111: begin    // bgeu
                            alu_ctr = 4'b0011;
                            pc_sel  = (~result[0]) ? 2'b01 : 2'b00;
                        end
                        default: begin
                            alu_ctr = 4'b0000;
                            pc_sel  = 2'b00;
                        end
                    endcase
                end
                5'b11011: begin    // jal
                    ext_op    = 3'b100;
                    reg_wen   = 1'b1;
                    alu_ctr   = 4'b0000;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b1;
                    alu_bsrc  = 2'b01;
                    mem2reg   = 1'b0;
                    pc_sel    = 2'b01;
                    width_sel = 3'b011;
                end
                5'b11001: begin    // jalr
                    ext_op    = 3'b000;
                    reg_wen   = 1'b1;
                    alu_ctr   = 4'b0000;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b1;
                    alu_bsrc  = 2'b01;
                    mem2reg   = 1'b0;
                    pc_sel    = 2'b10;
                    width_sel = 3'b011;
                end
                5'b00000: begin    // Loads
                    ext_op    = 3'b000;
                    reg_wen   = 1'b1;
                    alu_ctr   = 4'b0000;
                    mem_ren   = 1'b1;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b0;
                    alu_bsrc  = 2'b10;
                    mem2reg   = 1'b1;
                    pc_sel    = 2'b00;
                    width_sel = f3;
                end
                5'b01000: begin    // Stores
                    ext_op    = 3'b010;
                    reg_wen   = 1'b0;
                    alu_ctr   = 4'b0000;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b1;
                    alu_asrc  = 1'b0;
                    alu_bsrc  = 2'b10;
                    mem2reg   = 1'b0;    // 可取任意值
                    pc_sel    = 2'b00;
                    width_sel = f3;
                end
                /* 5'b11010: begin    // 输出a0寄存器的值(自定义)
                    ext_op    = 3'b000;
                    reg_wen   = 1'b0;
                    alu_ctr   = 4'b1111;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b0;
                    alu_bsrc  = 2'b00;
                    mem2reg   = 1'b0;      // 可取任意值
                    pc_sel    = 2'b00;
                    width_sel = 3'b000;    // 可取任意值
                    $write("%s", result);
                end */
                default: begin
                    ext_op    = 3'b111;
                    reg_wen   = 1'b0;
                    alu_ctr   = 4'b000;
                    mem_ren   = 1'b0;
                    mem_wen   = 1'b0;
                    alu_asrc  = 1'b0;
                    alu_bsrc  = 2'b00;
                    mem2reg   = 1'b0;
                    pc_sel    = 2'b00;
                    width_sel = 3'b011;
                end
            endcase
        end
    end


endmodule


module ysyx_210443_ctr_mem(
    input  wire              rst,

    // cpu接口
    input  wire [`BUS_WIDTH] mem_ren,
    input  wire              mem_wen,
    input  wire [`BUS_WIDTH] addr,
    input  wire [`BUS_WIDTH] data_in,
    input  wire [2 :      0] width_sel,
    output reg  [`BUS_WIDTH] data_out,

    // mem接口
    input  wire [`BUS_WIDTH] mem_rdata,
    output reg  [`BUS_WIDTH] mem_wdata,
    output reg  [`BUS_WIDTH] mem_wmask
);  

    
    always @(*) begin
        if      (rst) begin
            data_out  = `ZERO_WORD;
            mem_wdata = `ZERO_WORD;
            mem_wmask = `ZERO_WORD;
        end
        else if (mem_ren) begin    // Loads
            mem_wdata = `ZERO_WORD;    // 可取任意值
            mem_wmask = `ZERO_WORD;    // 可取任意值
            case (width_sel[1 : 0])
                2'b00: begin    // lb, lbu
                    case (addr[2 : 0])
                        3'b000: begin
                            data_out = {56'b0, mem_rdata[7  :  0]};
                        end
                        3'b001: begin
                            data_out = {56'b0, mem_rdata[15 :  8]};
                        end
                        3'b010: begin
                            data_out = {56'b0, mem_rdata[23 : 16]};
                        end
                        3'b011: begin
                            data_out = {56'b0, mem_rdata[31 : 24]};
                        end
                        3'b100: begin
                            data_out = {56'b0, mem_rdata[39 : 32]};
                        end
                        3'b101: begin
                            data_out = {56'b0, mem_rdata[47 : 40]};
                        end
                        3'b110: begin
                            data_out = {56'b0, mem_rdata[55 : 48]};
                        end
                        3'b111: begin
                            data_out = {56'b0, mem_rdata[63 : 56]};
                        end
                    endcase
                end
                2'b01: begin    // lh, lhu
                    case (addr[2 : 1])
                        2'b00: begin
                            data_out = {48'b0, mem_rdata[15 :  0]};
                        end
                        2'b01: begin
                            data_out = {48'b0, mem_rdata[31 : 16]};
                        end
                        2'b10: begin
                            data_out = {48'b0, mem_rdata[47 : 32]};
                        end
                        2'b11: begin
                            data_out = {48'b0, mem_rdata[63 : 48]};
                        end
                    endcase
                end
                2'b10: begin    // lw, lwu
                    case (addr[2])
                        1'b0: begin
                            data_out = {32'b0, mem_rdata[31 :  0]};
                        end 
                        1'b1: begin
                            data_out = {32'b0, mem_rdata[63 : 32]};
                        end
                    endcase
                end
                2'b11: begin    // ld
                    data_out = mem_rdata;
                end 
            endcase            
        end
        else if (mem_wen) begin    // Stores
            data_out = `ZERO_WORD;
            case (width_sel[1 : 0])
                2'b00: begin    // sb
                    case (addr[2 : 0])
                        3'b000: begin
                            mem_wdata = data_in;
                            mem_wmask = 64'h00000000_000000ff;
                        end
                        3'b001: begin
                            mem_wdata = (data_in << 64'h8);
                            mem_wmask = 64'h00000000_0000ff00;
                        end
                        3'b010: begin
                            mem_wdata = (data_in << 64'h10);
                            mem_wmask = 64'h00000000_00ff0000;
                        end
                        3'b011: begin
                            mem_wdata = (data_in << 64'h18);
                            mem_wmask = 64'h00000000_ff000000;
                        end
                        3'b100: begin
                            mem_wdata = (data_in << 64'h20);
                            mem_wmask = 64'h000000ff_00000000;
                        end
                        3'b101: begin
                            mem_wdata = (data_in << 64'h28);
                            mem_wmask = 64'h0000ff00_00000000;
                        end
                        3'b110: begin
                            mem_wdata = (data_in << 64'h30);
                            mem_wmask = 64'h00ff0000_00000000;
                        end
                        3'b111: begin
                            mem_wdata = (data_in << 64'h38);
                            mem_wmask = 64'hff000000_00000000;
                        end
                    endcase
                end
                2'b01: begin    // sh
                    case (addr[2 : 1])
                        2'b00: begin
                            mem_wdata = data_in;
                            mem_wmask = 64'h00000000_0000ffff;
                        end
                        2'b01: begin
                            mem_wdata = (data_in << 64'h10);
                            mem_wmask = 64'h00000000_ffff0000;
                        end
                        2'b10: begin
                            mem_wdata = (data_in << 64'h20);
                            mem_wmask = 64'h0000ffff_00000000;
                        end
                        2'b11: begin
                            mem_wdata = (data_in << 64'h30);
                            mem_wmask = 64'hffff0000_00000000;
                        end
                    endcase
                end
                2'b10: begin    // sw
                    case (addr[2])
                        1'b0: begin
                            mem_wdata = data_in;
                            mem_wmask = 64'h00000000_ffffffff;
                        end 
                        1'b1: begin
                            mem_wdata = (data_in << 64'h20);
                            mem_wmask = 64'hffffffff_00000000;
                        end
                    endcase
                end
                2'b11: begin    // sd
                    mem_wdata = data_in;
                    mem_wmask = 64'hffffffff_ffffffff;
                end 
            endcase
        end
        else begin
            data_out  = `ZERO_WORD;
            mem_wdata = `ZERO_WORD;    // 可取任意值
            mem_wmask = `ZERO_WORD;    // 可取任意值
        end
    end
    

endmodule


module ysyx_210443_data_mem (
    input  wire              clk,
    input  wire              rst,
     
    input  wire [`BUS_WIDTH] addr,
    input  wire [`BUS_WIDTH] mem_wdata,
    input  wire              mem_ren,
    input  wire [`BUS_WIDTH] mem_wmask,
    input  wire              mem_wen,
    
    output reg  [`BUS_WIDTH] mem_rdata
);
    
    
    wire [`BUS_WIDTH] rdata;
    
    RAMHelper mem(
        .clk   (clk),
        .en    (mem_ren | mem_wen),
        .rIdx  ({3'b0, ((addr - `PC_START)  >> 2'b11)}),
        .rdata (rdata),
        .wIdx  ({3'b0, ((addr - `PC_START)  >> 2'b11)}),
        .wdata (mem_wdata),
        .wmask (mem_wmask),
        .wen   (mem_wen)
    );

    assign mem_rdata = (rst) ? `ZERO_WORD : rdata;


endmodule


module ysyx_210443_id (
    input  wire          rst,

    input  wire [31 : 0] inst,

    output reg  [4 :  0] op,
    output reg  [2 :  0] f3,
    output reg           f7,
    output reg  [4 :  0] rs1,
    output reg  [4 :  0] rs2,
    output reg  [4 :  0] rd
);


    assign op  = (rst) ? 5'b0 : inst[6  :  2];
    assign f3  = (rst) ? 3'b0 : inst[14 : 12];
    assign f7  = (rst) ? 1'b0 : inst[30]; 
    assign rs1 = (rst) ? 5'b0 : inst[19 : 15];
    assign rs2 = (rst) ? 5'b0 : inst[24 : 20];
    assign rd  = (rst) ? 5'b0 : inst[11 :  7];

    
endmodule


module ysyx_210443_ie (
    input  wire              rst,
    
    input  wire [31 : 0]     inst,
    input  wire [2  : 0]     ext_op,
    
    output reg  [`BUS_WIDTH] imm
);


    always @(*) begin
        if (rst) begin
            imm = `ZERO_WORD;
        end
        else begin 
            case (ext_op)
                3'b000: begin    // immI
                    imm = {{52{inst[31]}}, inst[31 : 20]};
                end
                3'b001: begin    // immU
                    imm = {{32{inst[31]}}, inst[31 : 12], 12'b0};
                end
                3'b010: begin    // immS
                    imm = {{52{inst[31]}}, inst[31 : 25], inst[11 : 7]};
                end
                3'b011: begin    // immB
                    imm = {{52{inst[31]}}, inst[7],       inst[30 : 25], inst[11 : 8], 1'b0};
                end
                3'b100: begin    // immJ
                    imm = {{44{inst[31]}}, inst[19 : 12], inst[20],      inst[30 : 21], 1'b0};
                end
                default: begin
                    imm  = `ZERO_WORD;
                end
            endcase
        end
    end

    
endmodule


module ysyx_210443_inst_rom (
    input  wire              clk,
    input  wire              rst,

    input  wire [`BUS_WIDTH] inst_addr,
    output reg  [31 :     0] inst
);
    
    
    reg [`BUS_WIDTH] rdata;

    RAMHelper rom (
        .clk   (clk),
        .en    (1'b1),
        .rIdx  ({3'b0, ((inst_addr - `PC_START) >> 2'b11)}),
        .rdata (rdata),
        .wIdx  (64'b0),
        .wdata (64'b0),
        .wmask (64'b0),
        .wen   (1'b0)
    );

    assign inst = (rst) ? 32'b0 : (inst_addr[2] ? rdata[63 : 32] : rdata[31 : 0]);


endmodule


module ysyx_210443_mux_alu_ia (
    input  wire              rst,
    
    input  wire [`BUS_WIDTH] inst_addr,
    input  wire [`BUS_WIDTH] r_data1,
    input  wire              alu_asrc,

    output reg  [`BUS_WIDTH] alu_ia
);


    always @(*) begin
        if (rst) begin
            alu_ia = `ZERO_WORD;
        end
        else begin
            case (alu_asrc)
                1'b0: begin
                    alu_ia = r_data1;
                end
                1'b1: begin
                    alu_ia = inst_addr;
                end
            endcase
        end
    end


endmodule


module ysyx_210443_mux_alu_ib (
    input  wire              rst,
    
    input  wire [`BUS_WIDTH] r_data2,
    input  wire [`BUS_WIDTH] imm,
    input  wire [1 :      0] alu_bsrc,

    output reg  [`BUS_WIDTH] alu_ib
);


    always @(*) begin
        if (rst) begin
            alu_ib = `ZERO_WORD;
        end
        else begin
            case (alu_bsrc)
                2'b00: begin
                    alu_ib = r_data2;
                end
                2'b01: begin
                    alu_ib = 64'b100;
                end
                2'b10: begin
                    alu_ib = imm;
                end
                default: begin
                    alu_ib = `ZERO_WORD;
                end
            endcase
        end
    end

    
endmodule


module ysyx_210443_mux_offset (
    input  wire              rst,
    
    input  wire [`BUS_WIDTH] imm,
    input  wire [`BUS_WIDTH] r_data1,
    input  wire [`BUS_WIDTH] inst_addr,
    input  wire [1 :      0] pc_sel,

    output reg  [`BUS_WIDTH] offset
);


    always @(*) begin
        if (rst) begin
            offset = `ZERO_WORD;
        end
        else begin
            case (pc_sel)
                2'b00: begin
                    offset = 64'b100;
                end
                2'b01: begin
                    offset = imm;
                end
                2'b10: begin    // jalr
                    offset = (((r_data1 + imm) - inst_addr) & ~64'b1);
                end
                default: begin
                    offset = `ZERO_WORD;
                end
            endcase
        end
    end

    
endmodule


module ysyx_210443_mux_w_data (
    input  wire              rst,
    
    input  wire [`BUS_WIDTH] result,
    input  wire [`BUS_WIDTH] data_out,
    input  wire              mem2reg,

    output reg  [`BUS_WIDTH] w_data
);
    

    
    always @(*) begin
        if (rst) begin
            w_data = `ZERO_WORD;
        end
        else begin
            case (mem2reg)
                1'b0: begin
                    w_data = result;
                end
                1'b1: begin
                    w_data = data_out;
                end
            endcase
        end
    end


endmodule


module ysyx_210443_mux_width (
    input  wire              rst,
    input  wire [63 :     0] mux_width_i,
    input  wire [2  :     0] width_sel,

    output reg  [`BUS_WIDTH] mux_width_o
);


    always @(*) begin
        if (rst) begin
            mux_width_o = `ZERO_WORD;
        end
        else begin
            case (width_sel)
                3'b000: begin    // b
                    mux_width_o = {{56{mux_width_i[7]}},  mux_width_i[7 : 0]};
                end
                3'b100: begin    //bu
                    mux_width_o = {56'b0,                 mux_width_i[7 : 0]};
                end
                3'b001: begin    // h
                    mux_width_o = {{48{mux_width_i[15]}}, mux_width_i[15 : 0]};
                end
                3'b101: begin    // hu
                    mux_width_o = {48'b0,                 mux_width_i[15 : 0]};
                end
                3'b010: begin    //w
                    mux_width_o = {{32{mux_width_i[31]}}, mux_width_i[31 : 0]};
                end
                3'b110: begin    // wu
                    mux_width_o = {32'b0,                 mux_width_i[31 : 0]};
                end
                3'b011: begin    // d
                    mux_width_o = mux_width_i;
                end
                default: begin
                    mux_width_o = mux_width_i;
                end
            endcase
        end
    end

    
endmodule


module ysyx_210443_pc (
    input  wire              clk,
    input  wire              rst,
    
    input  wire [`BUS_WIDTH] offset,
    output reg  [`BUS_WIDTH] inst_addr
);


    parameter PC_START_RESET = `PC_START - 64'b100;

    always @(posedge clk) begin
        if (rst) begin
            inst_addr <= PC_START_RESET;
        end
        else begin
            inst_addr <= inst_addr + offset;
        end
    end


endmodule


module ysyx_210443_reg_file (
    input  wire              clk,
    input  wire              rst,

    input  wire [4 :      0] w_addr,
    input  wire [`BUS_WIDTH] w_data,
    input  wire              reg_wen,

    input  wire [4 :      0] r_addr1,
    output reg  [`BUS_WIDTH] r_data1,

    input  wire [4 :      0] r_addr2,
    output reg  [`BUS_WIDTH] r_data2,

    output wire [`BUS_WIDTH] regs_o[0 : 31]    // difftest
);


    reg [`BUS_WIDTH] regs[0 : 31];

    always @(posedge clk) begin    // 寄存器写入
        if (rst) begin   
            regs[0]  <= `ZERO_WORD;
			regs[1]  <= `ZERO_WORD;
			regs[2]  <= `ZERO_WORD;
			regs[3]  <= `ZERO_WORD;
			regs[4]  <= `ZERO_WORD;
			regs[5]  <= `ZERO_WORD;
			regs[6]  <= `ZERO_WORD;
			regs[7]  <= `ZERO_WORD;
			regs[8]  <= `ZERO_WORD;
			regs[9]  <= `ZERO_WORD;
			regs[10] <= `ZERO_WORD;
			regs[11] <= `ZERO_WORD;
			regs[12] <= `ZERO_WORD;
			regs[13] <= `ZERO_WORD;
			regs[14] <= `ZERO_WORD;
			regs[15] <= `ZERO_WORD;
			regs[16] <= `ZERO_WORD;
			regs[17] <= `ZERO_WORD;
			regs[18] <= `ZERO_WORD;
			regs[19] <= `ZERO_WORD;
			regs[20] <= `ZERO_WORD;
			regs[21] <= `ZERO_WORD;
			regs[22] <= `ZERO_WORD;
			regs[23] <= `ZERO_WORD;
			regs[24] <= `ZERO_WORD;
			regs[25] <= `ZERO_WORD;
			regs[26] <= `ZERO_WORD;
			regs[27] <= `ZERO_WORD;
			regs[28] <= `ZERO_WORD;
			regs[29] <= `ZERO_WORD;
			regs[30] <= `ZERO_WORD;
			regs[31] <= `ZERO_WORD;
        end
        else begin
            if ((reg_wen == 1'b1) && (w_addr != 5'b0)) begin    // x0寄存器不能改写
                regs[w_addr] <= w_data;
            end
        end
    end

    always @(*) begin    // 寄存器读出
        if (rst == 1'b1) begin
            r_data1 = `ZERO_WORD;
			r_data2 = `ZERO_WORD;
        end
        else begin
            r_data1 = regs[r_addr1];
			r_data2 = regs[r_addr2];
        end
    end

	genvar i;
	generate
		for (i = 0; i < 32; i = i + 1) begin    // 寄存器状态提交
			assign regs_o[i] = (reg_wen & (w_addr == i) & (i != 0)) ? w_data : regs[i];
		end
	endgenerate
    

endmodule
